#include <optional>
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib.h"
#include "json.hpp"
#include "leveldb/db.h"
#include <cassert>
#include <filesystem>
#include <fmt/core.h>
#include <folly/FBString.h>
#include <folly/FBVector.h>
#include <folly/executors/ThreadedExecutor.h>
#include <folly/futures/Future.h>
#include <folly/portability/GFlags.h>
#include <fstream>
#include <iostream>
#include <memory>
#include <sstream>
#include <unordered_map>
#include <vector>

using namespace folly;
using string = folly::fbstring;
template <class T> using vector = folly::fbvector<T>;
using json = nlohmann::json;

DEFINE_string(file_path, "server.json", "server json file path");
DEFINE_int32(port, 8080, "server port");

void foo(int x) { std::cout << "foo(" << x << ")" << std::endl; }
void do_foo() {
  folly::ThreadedExecutor executor;
  std::cout << "makeing Promise" << std::endl;
  Promise<int> p;
  Future<int> f = p.getSemiFuture().via(&executor);
  auto f2 = std::move(f).thenValue(foo);
  std::cout << "Future chain make" << std::endl;

  std::cout << "fulfilling Promise" << std::endl;
  p.setValue(42);
  std::move(f2).get();
  std::cout << "Promise fulfilled" << std::endl;
}

std::optional<json> find_by_id(std::vector<json> &v, const string &id) {
  for (auto &x : v) {
    if (x["id"] == id) {
      return x;
    }
  }
  return {};
}

inline std::string not_found() {
  return to_string(json::parse(R"(
  {
    "code": 404,
    "message": "record not found"
  }
)"));
}

constexpr bool is_json_same_type(const json &a, const json &b) noexcept {
  return (a.is_primitive() && b.is_primitive()) ||
         (a.is_structured() && b.is_structured()) ||
         (a.is_null() && b.is_null());
}

int main(int argc, char **argv) {
  // google::InitGoogleLogging(argv[0]);
  LOG(INFO) << "Connecting to server..." << std::endl;
  std::ifstream inputFile(
      FLAGS_file_path); // 打开名为 "example.txt" 的文件进行读取
  string json_content;
  if (inputFile.is_open()) {
    std::stringstream ss;
    ss << inputFile.rdbuf(); // 将文件内容读取到 stringstream 中
    json_content = string(ss.str());
    inputFile.close(); // 关闭文件
  } else {
    std::cerr << "无法打开文件" << std::endl;
    return 0;
  }
  json ex1 = json::parse(json_content);
  leveldb::DB *dbptr;
  // 如果数据未初始化则从json文件初始化数据
  std::filesystem::path path_to_db("serverdb");
  if (!std::filesystem::exists(path_to_db)) {
    leveldb::Options options;
    options.create_if_missing = true;
    leveldb::Status status = leveldb::DB::Open(options, "serverdb", &dbptr);
    if (!status.ok()) {
      std::cerr << status.ToString() << std::endl;
      return 0;
    }
    for (auto it = ex1.begin(); it != ex1.end(); it++) {
      auto key = it.key();
      auto value = it.value();
      std::cout << key << ". " << value << std::endl;
      dbptr->Put(leveldb::WriteOptions(), key, to_string(value));
    }
  } else {
    leveldb::Options options;
    options.create_if_missing = true;
    leveldb::Status status = leveldb::DB::Open(options, "serverdb", &dbptr);
    if (!status.ok()) {
      std::cerr << status.ToString() << std::endl;
      return 0;
    }
  }
  std::shared_ptr<leveldb::DB> db(dbptr);
  // HTTP
  httplib::Server svr;

  svr.Get("/:key", [&](const httplib::Request &req, httplib::Response &res) {
    auto key = req.path_params.at("key");
    std::string value;
    db->Get(leveldb::ReadOptions(), key, &value);
    res.set_content(value, "application/json");
  });
  svr.Post("/:key", [&](const httplib::Request &req, httplib::Response &res) {
    auto key = req.path_params.at("key");
    auto body = req.body;
    db->Put(leveldb::WriteOptions(), key, body);
    res.set_content(body, "application/json");
  });
  svr.Put("/:key", [&](const httplib::Request &req, httplib::Response &res) {
    auto key = req.path_params.at("key");
    auto body = req.body;
    db->Put(leveldb::WriteOptions(), key, body);
    res.set_content(body, "application/json");
  });
  svr.Delete("/:key", [&](const httplib::Request &req, httplib::Response &res) {
    auto key = req.path_params.at("key");
    std::string value;
    db->Get(leveldb::ReadOptions(), key, &value);
    res.set_content(value, "application/json");
  });
  svr.Get("/:key/:id",
          [&](const httplib::Request &req, httplib::Response &res) {
            auto key = req.path_params.at("key");
            auto id = req.path_params.at("id");
            std::string value;
            db->Get(leveldb::ReadOptions(), key, &value);
            json j(value);
            auto v = j.template get<std::vector<json>>();
            auto selected = find_by_id(v, id);
            if (selected.has_value()) {
              res.set_content(value, "application/json");
            } else {
              res.set_content(not_found(), "application/json");
            }
          });
  svr.Post("/:key/:id",
           [&](const httplib::Request &req, httplib::Response &res) {
             auto key = req.path_params.at("key");
             auto id = req.path_params.at("id");
             std::string value;
             db->Get(leveldb::ReadOptions(), key, &value);
             json j(value);
             auto v = j.template get<std::vector<json>>();
             auto selected = find_by_id(v, id);
             if (selected.has_value()) {
               res.set_content(value, "application/json");
               return;
             }
             j = json::parse(req.body);
             j["id"] = id;
             value = to_string(j);
             db->Put(leveldb::WriteOptions(), key, to_string(j));
             res.set_content(value, "application/json");
           });
  svr.Put("/:key/:id",
          [&](const httplib::Request &req, httplib::Response &res) {
            auto key = req.path_params.at("key");
            auto id = req.path_params.at("id");
            std::string value;
            db->Get(leveldb::ReadOptions(), key, &value);
            json j(value);
            auto v = j.template get<std::vector<json>>();
            for (auto &x : v) {
              if (x["id"] == id) {
                x = json::parse(req.body);
              }
            }
            value = to_string(j);
            db->Put(leveldb::WriteOptions(), key, to_string(j));
            res.set_content(value, "application/json");
          });
  svr.Delete("/:key/:id",
             [&](const httplib::Request &req, httplib::Response &res) {
               auto key = req.path_params.at("key");
               auto id = req.path_params.at("id");
               std::string value;
               db->Get(leveldb::ReadOptions(), key, &value);
               json j(value);
               auto v = j.template get<std::vector<json>>();
               auto selected = find_by_id(v, id);
               value = to_string(j);
               db->Put(leveldb::WriteOptions(), key, to_string(j));
               res.set_content(value, "application/json");
             });

  svr.listen("0.0.0.0", FLAGS_port);
  return 0;
}
